package main

import (
	"errors"
	"github.com/gin-gonic/gin"
	"math"
	"net/http"
)

// 请求参数

type Param struct {
	// 接收前端传递过来的参数.
	LoanMode   int     `json:"loanMode"`   //贷款模式 0 等额本金/ 1 等额本息
	LoanAmount float64 `json:"loanAmount"` // 贷款金额
	LoanMonths int     `json:"loanMonths"` // 贷款期数. 月份期数
	LoanRate   float64 `json:"loanRate"`   // 贷款年利率. 默认传递值: 0.031
}

// 每月还款信息

type MonthPay struct {
	SeqMonth         int     `json:"seqMonth"`         // 第几个月
	MonthPay         float64 `json:"monthPay"`         // 本月月供
	MonthPayLoan     float64 `json:"MonthPayLoan"`     // 本月本金
	MonthPayInterest float64 `json:"monthPayInterest"` // 本月利息
	ReleaseLoan      float64 `json:"releaseLoan"`      // 剩余本金 -- 贷款金额 - 已供本金.
	ReleaseTotal     float64 `json:"releaseTotal"`     // 剩余总额 -- 暂未计算.
}

func main() {
	e := gin.Default()

	e.POST("/api/loan", func(context *gin.Context) {
		//// 获取json传递的数据.
		var param Param
		err := context.BindJSON(&param)
		if err != nil {
			context.JSON(http.StatusBadRequest, gin.H{
				"msg": err,
			})
		}
		result, _ := Calc(param.LoanMode, param.LoanAmount, param.LoanMonths, param.LoanRate)
		result["loanMode"] = param.LoanMode
		result["loanAmount"] = param.LoanAmount
		result["loanMonths"] = param.LoanMonths
		result["loanRate"] = param.LoanRate
		context.JSON(200, gin.H{
			"msg":  "ok",
			"data": result,
		})
	})
	e.Run(":9090")
}

// 贷款类型.(0 等额本金/ 1 等额本息  -- > 能传到这里的都是商贷.)
// 贷款金额.
// 贷款月数.
// 贷款利率. 这里是年利率
// 返回,
// 每个月的还款金额. 每个月的还款金额列表,
// 总利息,总还款金额,

func Calc(loanmode int, loanAmount float64, loanMonths int, loanRate float64) (result map[string]interface{}, err error) {
	// loanmode = 0 等额本金,每个月还的本金一样.

	// loanmode = 1 等额本息,每个月还款额度一样.

	if loanmode != 0 && loanmode != 1 {
		return nil, errors.New("贷款类型传入不对.")
	}
	if loanmode == 0 {
		//log.Println("等额本金")
		// 等额本金,每个月本金一样.
		result = principalEquality(loanAmount, loanMonths, loanRate)
	} else {
		// 等额本息,每个与还款额度一样.
		//log.Println("等额本息")
		result = monthEquality(loanAmount, loanMonths, loanRate)
	}
	return result, err
}

// 贷款金额/年限(月数),月利率(年利率/12)
// 计算等额本息 每月的月供.
func calc(loanAmount float64, loanMonths int, loanRate float64) float64 {
	return loanAmount * math.Pow(1+loanRate/12, float64(loanMonths)) * loanRate / 12 / (math.Pow(1+loanRate/12, float64(loanMonths)) - 1)
}
func monthEquality(loanAmount float64, loanMonths int, loanRate float64) map[string]interface{} {
	// 等额本息,每个月还款一样
	// 1.计算每个月还款额度
	monthPayment := calc(loanAmount, loanMonths, loanRate)

	// 4.计算总还款
	// 贷款金额+ 总利息.
	totalMoney := monthPayment * float64(loanMonths)

	// 剩余本金.
	remainPrincipal := loanAmount

	// 2.计算每个月的列表.
	var monthlyPays []MonthPay
	var monthPay MonthPay
	for i := 1; i <= loanMonths; i++ {
		// 第 i个月
		monthPay.SeqMonth = i
		// 第i月的 月供.
		monthPay.MonthPay = math.Round(monthPayment*100) / 100

		// 第i月 还的利息.
		monthIn := remainPrincipal * loanRate / 12
		monthPay.MonthPayInterest = math.Round(monthIn*100) / 100

		// 第i月 还的本金.
		monthLoan := monthPayment - monthIn
		monthPay.MonthPayLoan = math.Round(monthLoan*100) / 100

		// 剩余本金
		remainPrincipal -= monthLoan
		monthPay.ReleaseLoan = math.Round(remainPrincipal*100) / 100

		monthPay.ReleaseTotal = math.Round((totalMoney-monthPayment*float64(i))*100) / 100

		monthlyPays = append(monthlyPays, monthPay)
	}

	// 3.计算总利息
	totalInterest := totalMoney - loanAmount

	result := make(map[string]interface{})
	result["monthPay"] = math.Round(monthPayment*100) / 100
	result["monthlyPays"] = monthlyPays
	result["totalMoney"] = math.Round(totalMoney*100) / 100
	result["totalInterest"] = math.Round(totalInterest*100) / 100
	return result
}

// 计算等额本金的每月月供
// seqmonths 表示第几个月.

func monthlyPay(loanAmount float64, totalmonth int, seqmonths int, monthlyRate float64) float64 {
	// 每个月还的本金一样.还指定期数.
	//每个月还本金 + 利息.
	//     本金 								  利息
	return loanAmount/float64(totalmonth) + (loanAmount-(float64(seqmonths)-1)*loanAmount/float64(totalmonth))*monthlyRate
}

func principalEquality(loanAmount float64, loanMonths int, loanRate float64) map[string]interface{} {
	// 等额本金. 每个月还款的本金一样.

	// 1.计算每个月应还的本金
	monthPrincipal := loanAmount / float64(loanMonths)

	// 4.计算总还款
	// 贷款金额+ 总利息.
	var totalMoney float64
	// 剩余本金.
	remainPrincipal := loanAmount

	// 2.计算每个月的列表.
	var monthlyPays []MonthPay
	var monthPay MonthPay
	for i := 1; i <= loanMonths; i++ {
		// 第i个月 的利息
		seqInterest := remainPrincipal * loanRate / 12
		// 第i个月 的月供
		seqMonthpay := monthPrincipal + seqInterest
		// 累计总还款
		totalMoney += seqMonthpay
		// 剩余本金
		remainPrincipal -= monthPrincipal

		monthPay.SeqMonth = i                                         // 第几个月
		monthPay.MonthPay = math.Round(seqMonthpay*100) / 100         // 月供
		monthPay.MonthPayLoan = math.Round(monthPrincipal*100) / 100  // 本月本金
		monthPay.MonthPayInterest = math.Round(seqInterest*100) / 100 // 本月支付的利息
		monthPay.ReleaseLoan = math.Round(remainPrincipal*100) / 100  // 剩余本金

		monthlyPays = append(monthlyPays, monthPay)
	}

	for i := 0; i < loanMonths; i++ {

		var allTotal float64
		for j := 0; j <= i; j++ {
			allTotal += monthlyPays[j].MonthPay
		}
		if i == loanMonths-1 {
			monthlyPays[i].ReleaseTotal = 0
		} else {
			monthlyPays[i].ReleaseTotal = math.Round((totalMoney-allTotal)*100) / 100

		}
	}
	// 3.计算总利息
	totalInterest := totalMoney - loanAmount

	result := make(map[string]interface{})
	result["monthPay"] = monthlyPays[0].MonthPay
	result["monthlyPays"] = monthlyPays
	result["totalMoney"] = math.Round(totalMoney*100) / 100
	result["totalInterest"] = math.Round(totalInterest*100) / 100
	return result
}
